package ru.spbu.math.seabattle.client.jms.gui;

import java.awt.Color;
import java.awt.Container;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JButton;
import javax.swing.JLabel;

import ru.spbu.math.seabattle.client.jms.SeaBattleClient;
import ru.spbu.math.seabattle.entity.Action;
import ru.spbu.math.seabattle.entity.FieldModel;
import ru.spbu.math.seabattle.entity.FieldModel.Cell;

public class FieldComponent implements IComponent {

	private static final int 		FIELD_SIZE = 10;

	private JButton[][] 			field = new JButton[FIELD_SIZE][FIELD_SIZE];
	private JLabel[] 				upperLabels = new JLabel[FIELD_SIZE];
	private JLabel[] 				leftLabels = new JLabel[FIELD_SIZE];
	private FieldModel 				fieldModel = new FieldModel(FIELD_SIZE);
	private ShipListener[][] 		shipListeners = new ShipListener[FIELD_SIZE][FIELD_SIZE];
	private ShootListener[][]		shootListeners = new ShootListener[FIELD_SIZE][FIELD_SIZE];
	private JLabel 					placeShipMessage = new JLabel();
	private int[] 					availableShips = { 4, 3, 2, 1 };
	private static final String[] 	shipName = { "Destroyer", "Cruiser",
												"Submarine", "Aircraft Carrier" };
	private final SeaBattleClient client;

	public FieldComponent(SeaBattleClient client) {

		this.client = client;
		
		for (int i = 0; i < FIELD_SIZE; i++) {
			upperLabels[i] = new JLabel((char) ((int) ('a') + i) + "");
			leftLabels[i] = new JLabel((i + 1) + "");
			for (int j = 0; j < FIELD_SIZE; j++) {
				field[i][j] = new JButton();
				shipListeners[i][j] = new ShipListener(i, j);
				shootListeners[i][j] = new ShootListener(i, j);
			}
		}

		placeShipMessage.setBounds(Constants.getMessageRectangle());
		placeShipMessage.setVisible(false);
	}

	@Override
	public void setBounds(Rectangle r) {

		int length = Math.min(r.height, r.width) / FIELD_SIZE;

		for (int i = 0; i < FIELD_SIZE; i++) {

			upperLabels[i].setBounds(r.x + i * length + length / 2, r.y
					- length / 2, length, length / 2);
			leftLabels[i].setBounds(r.x - length / 2, r.y + i * length,
					length / 2, length);

			for (int j = 0; j < FIELD_SIZE; j++) {
				field[i][j].setBounds(r.x + j * length, r.y + i * length,
						length, length);
			}
		}
	}

	@Override
	public void addToContainer(Container container) {

		for (int i = 0; i < FIELD_SIZE; i++) {
			container.add(upperLabels[i]);
			container.add(leftLabels[i]);
			for (int j = 0; j < FIELD_SIZE; j++) {
				container.add(field[i][j]);
			}
		}

		container.add(placeShipMessage);
	}

	public void setColor(int i, int j, Color color) {
		field[i][j].setBackground(color);
	}

	public void setBackground(Color color) {
		for (int i = 0; i < FIELD_SIZE; i++) {
			for (int j = 0; j < FIELD_SIZE; j++) {
				field[i][j].setBackground(color);
			}
		}
	}

	public void disableAll() {

		for (int i = 0; i < FIELD_SIZE; i++) {
			for (int j = 0; j < FIELD_SIZE; j++) {
				field[i][j].setEnabled(false);
			}
		}
	}

	private int occupiedCells = 0;
	private int currentShip = 4;

	private static enum Orientation {
		HORIZONTAL, VERTICAL,
	}

	private Orientation orientation;

	/**
	 * This is a very complicated class which provides placing the ships. You
	 * start placing ships beginning from 4-cell ship and then continue in
	 * descending order. When you click a button, this class provides you with
	 * available cells to continue placing ship. (don't watch)
	 */
	private class ShipListener implements MouseListener {

		private int i;
		private int j;

		public ShipListener(int i, int j) {
			this.i = i;
			this.j = j;
		}

		@Override
		public void mouseClicked(MouseEvent arg0) {

			occupiedCells++;
			field[i][j].setBackground(Constants
					.getShipColor());
			fieldModel.setShipCell(i, j);

			removeListeners();
			disableAll();

			if (occupiedCells > currentShip - 1) {

				for (int ii = 0; ii < FIELD_SIZE; ii++) {
					for (int jj = 0; jj < FIELD_SIZE; jj++) {
						if (!fieldModel.isAvailableCell(ii, jj)) {
							shipListeners[ii][jj] = null;
						}
					}
				}

				availableShips[currentShip - 1]--;
				if (availableShips[currentShip - 1] > 0) {
					System.out.println("here");
					placeShip();
					return;
				} else {
					currentShip--;
					if (currentShip <= 0) {
						removeListeners();
						disableAll();
						placeShipMessage.setVisible(false);
					//	surroundShipsByShotCells();
						client.onShipPlaced(fieldModel);
						return;
					} else {
						placeShip();
					}
				}

				return;
			}

			if (occupiedCells == 1) {
				if (i > 0
						&& shipListeners[i - 1][j] != null) {
					field[i - 1][j].setEnabled(true);
					field[i - 1][j].addMouseListener(shipListeners[i - 1][j]);
				}

				if (i < FIELD_SIZE - 1
						&& shipListeners[i + 1][j] != null) {
					field[i + 1][j].setEnabled(true);
					field[i + 1][j]
							.addMouseListener(shipListeners[i + 1][j]);
				}

				if (j < FIELD_SIZE - 1
						&& shipListeners[i][j + 1] != null) {
					field[i][j + 1].setEnabled(true);
					field[i][j + 1]
							.addMouseListener(shipListeners[i][j + 1]);
				}

				if (j > 0
						&& shipListeners[i][j - 1] != null) {
					field[i][j - 1].setEnabled(true);
					field[i][j - 1]
							.addMouseListener(shipListeners[i][j - 1]);
				}
				return;
			}

			if (occupiedCells == 2) {
				if (i > 0
						&& fieldModel.isShipCell(i - 1, j)
						|| i < FIELD_SIZE - 1
						&& fieldModel.isShipCell(i + 1, j)) {
					orientation = Orientation.HORIZONTAL;
				} else {
					orientation = Orientation.VERTICAL;
				}
			}

			if (occupiedCells > 1) {
				if (orientation == Orientation.HORIZONTAL) {
					int x = i - 1;
					while (x >= 0 && shipListeners[x][j] != null) {
						if (fieldModel.isShipCell(x, j)) {
							x--;
							continue;
						} else {
							field[x][j]
									.addMouseListener(shipListeners[x][j]);
							field[x][j].setEnabled(true);
							break;
						}
					}

					x = i + 1;
					while (x < FIELD_SIZE
							&& shipListeners[x][j] != null) {
						if (fieldModel.isShipCell(x, j)) {
							x++;
							continue;
						} else {
							field[x][j]
									.addMouseListener(shipListeners[x][j]);
							field[x][j].setEnabled(true);
							break;
						}
					}
				} else {
					int y = j - 1;
					while (y >= 0 && shipListeners[i][y] != null) {
						if (fieldModel.isShipCell(i, y)) {
							y--;
							continue;
						} else {
							field[i][y]
									.addMouseListener(shipListeners[i][y]);
							field[i][y].setEnabled(true);
							break;
						}
					}

					y = j + 1;
					while (y < FIELD_SIZE
							&& shipListeners[i][y] != null) {
						if (fieldModel.isShipCell(i, y)) {
							y++;
							continue;
						} else {
							field[i][y]
									.addMouseListener(shipListeners[i][y]);
							field[i][y].setEnabled(true);
							break;
						}
					}
				}
			}
		}

		// some shit methods that we unfortunately must override
		@Override 	public void mouseEntered(MouseEvent arg0) {}
		@Override	public void mouseExited(MouseEvent arg0) {}
		@Override	public void mousePressed(MouseEvent arg0) {}
		@Override	public void mouseReleased(MouseEvent arg0) {}
	}
	
	private class ShootListener implements MouseListener {

		int i;
		int j;
		
		public ShootListener(int i, int j) {
			this.i = i;
			this.j = j;
		}
		
		@Override
		public void mouseClicked(MouseEvent arg0) {
			
			field[i][j].removeMouseListener(shootListeners[i][j]);
			System.out.println("==========================");
			System.out.println("FIELD_COMPOMEMT: SHOOT_LISTENER: (" + client.getName() + ") SHOT: " + i + " " + j);
			
			client.onShot(new Action(i, j));
		}

		@Override public void mouseEntered(MouseEvent arg0) {}
		@Override public void mouseExited(MouseEvent arg0) {}
		@Override public void mousePressed(MouseEvent arg0) {}
		@Override public void mouseReleased(MouseEvent arg0) {}
	}

	private void removeListeners() {
		for (int i = 0; i < FIELD_SIZE; i++) {
			for (int j = 0; j < FIELD_SIZE; j++) {
				field[i][j].removeMouseListener(shipListeners[i][j]);
			}
		}
	}

	public void enableAllPossible() {
		disableAll();
		removeListeners();

		for (int i = 0; i < FIELD_SIZE; i++) {
			for (int j = 0; j < FIELD_SIZE; j++) {
				if (fieldModel.isAvailableCell(i, j)) {
					field[i][j].addMouseListener(shipListeners[i][j]);
					field[i][j].setEnabled(true);
				}
			}
		}
	}

	private void addListeners() {
		enableAllPossible();
	}
	
	public void surroundShipsByShotCells() {
		
		for (int i = 0; i < FIELD_SIZE; i++) {
			for (int j = 0; j < FIELD_SIZE; j++) {
				if (!fieldModel.isAvailableCell(i, j) && !fieldModel.isShipCell(i, j)) {
					fieldModel.setShotCell(i, j);
					field[i][j].setBackground(Constants.getShotCellColor());
				}
			}
		}
	}

	public void placeShip() {
		
		occupiedCells = 0;
		orientation = null;
		placeShipMessage.setText("Place your " + shipName[currentShip - 1]
				+ " (" + currentShip + " cell"
				+ ((currentShip == 1) ? ")." : "s)."));
		addListeners();
		placeShipMessage.setVisible(true);
	}
	
	public void addListenersToEnemy() {
		removeEnemyListeners();
		for (int i = 0; i < FIELD_SIZE; i++) {
			for (int j = 0; j < FIELD_SIZE; j++) {
				if (!fieldModel.isShotCell(i, j) && !fieldModel.isDemagedShipCell(i, j)) {
					field[i][j].addMouseListener(shootListeners[i][j]);
					field[i][j].setEnabled(true);
				}
			}
		}
	}
	
	public void setDemagedShip(int i, int j) {
		fieldModel.setCell(i, j, Cell.DAMAGED_SHIP);
	}
	
	public void setShotCell(int i, int j) {
		fieldModel.setCell(i, j, Cell.SHOT_CELL);
	}
	
	public void removeEnemyListeners() {
		for (int i = 0; i < FIELD_SIZE; i++) {
			for (int j = 0; j < FIELD_SIZE; j++) {
				MouseListener[] list = field[i][j].getMouseListeners();
				//if (!fieldModel.isShotCell(i, j) && !fieldModel.isDemagedShipCell(i, j));
				for (MouseListener ml : list) {
					field[i][j].removeMouseListener(ml);
				}
				field[i][j].setEnabled(false);
			}
		}
	}
}